利用{rayshader}生成全球任意位置的三维地形图. refer to https://github.com/camartinezbu/30DayChartChallenge2022/blob/f7f7cb1337f2604da2b78d7c920cb318606cf4c6/14-3-dimensional/scripts/1-plot.R

Load libraries

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6     ✔ purrr   0.3.4
✔ tibble  3.1.7     ✔ dplyr   1.0.9
✔ tidyr   1.2.0     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.1
── Conflicts ─────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(osmdata)
Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(sf)
Linking to GEOS 3.9.1, GDAL 3.3.2, PROJ 7.2.1; sf_use_s2() is TRUE
library(rayshader)
library(raster)
载入需要的程辑包:sp

载入程辑包:‘raster’

The following object is masked from ‘package:dplyr’:

    select
library(elevatr)

Create Bounding Box

定义地理区域范围.


# map center longitude and latitude
center_lon_lat <- c(102.929, 30.150)
half_width <- 0.05

center_lon_lat <- c(116.321,39.947)
half_width <- 0.1

med_bbox <- st_bbox(c(xmin = center_lon_lat[1]-half_width, xmax = center_lon_lat[1]+half_width, 
                      ymin = center_lon_lat[2]-half_width, ymax = center_lon_lat[2]+half_width),
                      crs = 4326)

med_bbox_df <- data.frame(x = c(center_lon_lat[1]-half_width, center_lon_lat[1]+half_width),
                          y = c(center_lon_lat[2]-half_width, center_lon_lat[2]+half_width))


extent_zoomed <- raster::extent(med_bbox)

Get elevation data

利用{elevatr}下载地形数据.

prj_dd <- "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"

elev_med <- get_elev_raster(med_bbox_df, prj=prj_dd, z=12, clip="bbox")

 Accessing raster elevation [-------------------------]   0%
 Accessing raster elevation [=>-----------------------]   8%
 Accessing raster elevation [===>---------------------]  17%
 Accessing raster elevation [=====>-------------------]  25%
 Accessing raster elevation [=======>-----------------]  33%
 Accessing raster elevation [=========>---------------]  42%
 Accessing raster elevation [===========>-------------]  50%
 Accessing raster elevation [==============>----------]  58%
 Accessing raster elevation [================>--------]  67%
 Accessing raster elevation [==================>------]  75%
 Accessing raster elevation [====================>----]  83%
 Accessing raster elevation [======================>--]  92%
 Accessing raster elevation [=========================] 100%
Mosaicing & Projecting
Clipping DEM to bbox
Note: Elevation units are in meters.
elev_med_mat <- raster_to_matrix(elev_med)
[1] "Dimensions of matrix are: 1359x1358"

Get OpenStreetMap(OSM) data

从OpenStreetMap获得地理信息数据


# Get highway data
# https://wiki.openstreetmap.org/wiki/Map_features#Highway
med_highway <- med_bbox %>%  opq() %>%
  add_osm_feature("highway",
    c("motorway", "trunk", "primary", "secondary", "tertiary")) %>%
  osmdata_sf()
med_highway_lines <- med_highway$osm_lines

# Get river data
# https://wiki.openstreetmap.org/wiki/Map_features#Waterway
med_rivers <- med_bbox %>% opq() %>%
  add_osm_feature("waterway",
    c("river", "stream")) %>%
  osmdata_sf()
med_river_lines <- med_rivers$osm_lines

# Get buildings data
med_buildings <- med_bbox %>% opq() %>%
  add_osm_feature("building") %>%
  osmdata_sf()
med_building_polygons <- med_buildings$osm_polygons

# Get parkings data
med_parkdings <- med_bbox %>% opq() %>%
  add_osm_feature("parking") %>%
  osmdata_sf()
med_parking_polygons <- med_parkdings$osm_polygons

# Get tourism data
med_tourisms <- med_bbox %>% opq() %>%
  add_osm_feature("tourism") %>%
  osmdata_sf()
med_tourism_polygons <- med_tourisms$osm_polygons

# Get place data
med_places <- med_bbox %>% opq() %>%
  add_osm_feature("place") %>%
  osmdata_sf()
med_places_points <- med_places$osm_points %>%
  filter(place %in% c("town","city"))

Draw base map

base_map <- elev_med_mat %>% 
  height_shade() %>%
  add_overlay(sphere_shade(elev_med_mat, texture = "desert", colorintensity = 5), alphalayer=0.5) %>%
  add_shadow(lamb_shade(elev_med_mat), 0) %>%
  add_shadow(ambient_shade(elev_med_mat),0) %>% 
  add_shadow(texture_shade(elev_med_mat,detail=8/10,contrast=9,brightness = 11), 0.1)

plot_map(base_map)

Add OpenStreetMap(OSM) data


# create highway layer
med_road_01 <- med_highway_lines %>%
  filter(highway %in% c("motorway","trunk"))
med_road_02 <- med_highway_lines %>%
  filter(highway %in% c("primary", "secondary"))
med_road_03 <- med_highway_lines %>%
  filter(highway %in% c("tertiary"))

road_layer <- generate_line_overlay(med_road_03,extent = extent_zoomed,
                                    linewidth = 2, color="white", 
                                    heightmap = elev_med_mat) %>% 
  add_overlay(generate_line_overlay(med_road_02,extent = extent_zoomed,
                                    linewidth = 1, color="white", lty=3,
                                    heightmap = elev_med_mat)) %>% 
  add_overlay(generate_line_overlay(med_road_01,extent = extent_zoomed,
                                    linewidth = 3, color="white",
                                    heightmap = elev_med_mat))

# create wateray layer
med_river_01 <- med_river_lines %>%
  filter(waterway %in% c("river"))
med_river_02 <- med_river_lines %>%
  filter(waterway %in% c("stream"))

water_layer <- generate_line_overlay(med_river_01,extent = extent_zoomed,
                                     linewidth = 2, color="skyblue2", 
                                     heightmap = elev_med_mat) %>%
  add_overlay(generate_line_overlay(med_river_02,extent = extent_zoomed,
                                    linewidth = 1, color="skyblue2", lty=3,
                                    heightmap = elev_med_mat))
# polygon_layer = generate_polygon_overlay(med_parking_polygons, extent = extent_zoomed,
#                                          heightmap = elev_med_mat, palette="grey30") %>%
#   add_overlay(generate_polygon_overlay(med_building_polygons, extent = extent_zoomed,
#                                        heightmap = elev_med_mat, palette="darkred")) %>% 
#   add_overlay(generate_polygon_overlay(med_tourism_polygons, extent = extent_zoomed,
#                                        heightmap = elev_med_mat, palette="darkgreen"), alphalayer = 0.6)
polygon_layer = generate_polygon_overlay(med_building_polygons, extent = extent_zoomed,
                                       heightmap = elev_med_mat, palette="darkred")
Error in s2_geography_from_wkb(x, oriented = oriented, check = check) : 
  Evaluation error: Found 1 feature with invalid spherical geometry.
[28669] Loop 0 is not valid: Edge 7 crosses edge 10.

base_map %>% 
#  add_overlay(polygon_layer) %>%
  add_overlay(water_layer, alphalayer = 0.8) %>% 
  add_overlay(road_layer) %>%
  add_overlay(generate_label_overlay(med_places_points, extent = extent_zoomed,
                                     text_size = 1, point_size = 1, color = "white",
                                     halo_color = "black", halo_expand = 10, 
                                     halo_blur = 20, halo_alpha = 0.8,
                                     seed=1, heightmap = elev_med_mat, data_label_column = "name.en")) %>%
  plot_map()

base_map %>% 
#  add_overlay(polygon_layer) %>%
  add_overlay(water_layer, alphalayer = 0.8) %>% 
  add_overlay(road_layer) %>%
  add_overlay(generate_label_overlay(med_places_points, extent = extent_zoomed,
                                     text_size = 1, point_size = 1, color = "white",
                                     halo_color = "black", halo_expand = 10, 
                                     halo_blur = 15, halo_alpha = 0.8,
                                     seed=1, heightmap = elev_med_mat, data_label_column = "name.en")) %>%
  plot_3d(elev_med_mat, windowsize=c(1200,1100), zscale=20, theta=20, phi=30, fov=45, zoom=0.6)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoK5Yip55SoYHtyYXlzaGFkZXJ9YOeUn+aIkOWFqOeQg+S7u+aEj+S9jee9rueahOS4iee7tOWcsOW9ouWbvi4KcmVmZXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL2NhbWFydGluZXpidS8zMERheUNoYXJ0Q2hhbGxlbmdlMjAyMi9ibG9iL2Y3ZjdjYjEzMzdmMjYwNGRhMmI3OGQ3YzkyMGNiMzE4NjA2Y2Y0YzYvMTQtMy1kaW1lbnNpb25hbC9zY3JpcHRzLzEtcGxvdC5SCgojIyBMb2FkIGxpYnJhcmllcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3NtZGF0YSkKbGlicmFyeShzZikKbGlicmFyeShyYXlzaGFkZXIpCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGVsZXZhdHIpCmBgYAoKIyMgQ3JlYXRlIEJvdW5kaW5nIEJveAoK5a6a5LmJ5Zyw55CG5Yy65Z+f6IyD5Zu0LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgbWFwIGNlbnRlciBsb25naXR1ZGUgYW5kIGxhdGl0dWRlCmNlbnRlcl9sb25fbGF0IDwtIGMoMTAyLjkyOSwgMzAuMTUwKQpoYWxmX3dpZHRoIDwtIDAuMDUKCmNlbnRlcl9sb25fbGF0IDwtIGMoMTE2LjMyMSwzOS45NDcpCmhhbGZfd2lkdGggPC0gMC4xCgptZWRfYmJveCA8LSBzdF9iYm94KGMoeG1pbiA9IGNlbnRlcl9sb25fbGF0WzFdLWhhbGZfd2lkdGgsIHhtYXggPSBjZW50ZXJfbG9uX2xhdFsxXStoYWxmX3dpZHRoLCAKICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSBjZW50ZXJfbG9uX2xhdFsyXS1oYWxmX3dpZHRoLCB5bWF4ID0gY2VudGVyX2xvbl9sYXRbMl0raGFsZl93aWR0aCksCiAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQoKbWVkX2Jib3hfZGYgPC0gZGF0YS5mcmFtZSh4ID0gYyhjZW50ZXJfbG9uX2xhdFsxXS1oYWxmX3dpZHRoLCBjZW50ZXJfbG9uX2xhdFsxXStoYWxmX3dpZHRoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYyhjZW50ZXJfbG9uX2xhdFsyXS1oYWxmX3dpZHRoLCBjZW50ZXJfbG9uX2xhdFsyXStoYWxmX3dpZHRoKSkKCgpleHRlbnRfem9vbWVkIDwtIHJhc3Rlcjo6ZXh0ZW50KG1lZF9iYm94KQpgYGAKCiMjIEdldCBlbGV2YXRpb24gZGF0YQoK5Yip55SoYHtlbGV2YXRyfWDkuIvovb3lnLDlvaLmlbDmja4uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwcmpfZGQgPC0gIitwcm9qPWxvbmdsYXQgK2VsbHBzPVdHUzg0ICtkYXR1bT1XR1M4NCArbm9fZGVmcyIKCmVsZXZfbWVkIDwtIGdldF9lbGV2X3Jhc3RlcihtZWRfYmJveF9kZiwgcHJqPXByal9kZCwgej0xMiwgY2xpcD0iYmJveCIpCgplbGV2X21lZF9tYXQgPC0gcmFzdGVyX3RvX21hdHJpeChlbGV2X21lZCkKYGBgCgojIyBHZXQgT3BlblN0cmVldE1hcChPU00pIGRhdGEKCuS7jk9wZW5TdHJlZXRNYXDojrflvpflnLDnkIbkv6Hmga/mlbDmja4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIEdldCBoaWdod2F5IGRhdGEKIyBodHRwczovL3dpa2kub3BlbnN0cmVldG1hcC5vcmcvd2lraS9NYXBfZmVhdHVyZXMjSGlnaHdheQptZWRfaGlnaHdheSA8LSBtZWRfYmJveCAlPiUgIG9wcSgpICU+JQogIGFkZF9vc21fZmVhdHVyZSgiaGlnaHdheSIsCiAgICBjKCJtb3RvcndheSIsICJ0cnVuayIsICJwcmltYXJ5IiwgInNlY29uZGFyeSIsICJ0ZXJ0aWFyeSIpKSAlPiUKICBvc21kYXRhX3NmKCkKbWVkX2hpZ2h3YXlfbGluZXMgPC0gbWVkX2hpZ2h3YXkkb3NtX2xpbmVzCgojIEdldCByaXZlciBkYXRhCiMgaHR0cHM6Ly93aWtpLm9wZW5zdHJlZXRtYXAub3JnL3dpa2kvTWFwX2ZlYXR1cmVzI1dhdGVyd2F5Cm1lZF9yaXZlcnMgPC0gbWVkX2Jib3ggJT4lIG9wcSgpICU+JQogIGFkZF9vc21fZmVhdHVyZSgid2F0ZXJ3YXkiLAogICAgYygicml2ZXIiLCAic3RyZWFtIikpICU+JQogIG9zbWRhdGFfc2YoKQptZWRfcml2ZXJfbGluZXMgPC0gbWVkX3JpdmVycyRvc21fbGluZXMKCiMgR2V0IGJ1aWxkaW5ncyBkYXRhCm1lZF9idWlsZGluZ3MgPC0gbWVkX2Jib3ggJT4lIG9wcSgpICU+JQogIGFkZF9vc21fZmVhdHVyZSgiYnVpbGRpbmciKSAlPiUKICBvc21kYXRhX3NmKCkKbWVkX2J1aWxkaW5nX3BvbHlnb25zIDwtIG1lZF9idWlsZGluZ3Mkb3NtX3BvbHlnb25zCgojIEdldCBwYXJraW5ncyBkYXRhCm1lZF9wYXJrZGluZ3MgPC0gbWVkX2Jib3ggJT4lIG9wcSgpICU+JQogIGFkZF9vc21fZmVhdHVyZSgicGFya2luZyIpICU+JQogIG9zbWRhdGFfc2YoKQptZWRfcGFya2luZ19wb2x5Z29ucyA8LSBtZWRfcGFya2RpbmdzJG9zbV9wb2x5Z29ucwoKIyBHZXQgdG91cmlzbSBkYXRhCm1lZF90b3VyaXNtcyA8LSBtZWRfYmJveCAlPiUgb3BxKCkgJT4lCiAgYWRkX29zbV9mZWF0dXJlKCJ0b3VyaXNtIikgJT4lCiAgb3NtZGF0YV9zZigpCm1lZF90b3VyaXNtX3BvbHlnb25zIDwtIG1lZF90b3VyaXNtcyRvc21fcG9seWdvbnMKCiMgR2V0IHBsYWNlIGRhdGEKbWVkX3BsYWNlcyA8LSBtZWRfYmJveCAlPiUgb3BxKCkgJT4lCiAgYWRkX29zbV9mZWF0dXJlKCJwbGFjZSIpICU+JQogIG9zbWRhdGFfc2YoKQptZWRfcGxhY2VzX3BvaW50cyA8LSBtZWRfcGxhY2VzJG9zbV9wb2ludHMgJT4lCiAgZmlsdGVyKHBsYWNlICVpbiUgYygidG93biIsImNpdHkiKSkKYGBgCgojIyBEcmF3IGJhc2UgbWFwCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJhc2VfbWFwIDwtIGVsZXZfbWVkX21hdCAlPiUgCiAgaGVpZ2h0X3NoYWRlKCkgJT4lCiAgYWRkX292ZXJsYXkoc3BoZXJlX3NoYWRlKGVsZXZfbWVkX21hdCwgdGV4dHVyZSA9ICJkZXNlcnQiLCBjb2xvcmludGVuc2l0eSA9IDUpLCBhbHBoYWxheWVyPTAuNSkgJT4lCiAgYWRkX3NoYWRvdyhsYW1iX3NoYWRlKGVsZXZfbWVkX21hdCksIDApICU+JQogIGFkZF9zaGFkb3coYW1iaWVudF9zaGFkZShlbGV2X21lZF9tYXQpLDApICU+JSAKICBhZGRfc2hhZG93KHRleHR1cmVfc2hhZGUoZWxldl9tZWRfbWF0LGRldGFpbD04LzEwLGNvbnRyYXN0PTksYnJpZ2h0bmVzcyA9IDExKSwgMC4xKQoKcGxvdF9tYXAoYmFzZV9tYXApCmBgYAoKIyMgQWRkIE9wZW5TdHJlZXRNYXAoT1NNKSBkYXRhCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBjcmVhdGUgaGlnaHdheSBsYXllcgptZWRfcm9hZF8wMSA8LSBtZWRfaGlnaHdheV9saW5lcyAlPiUKICBmaWx0ZXIoaGlnaHdheSAlaW4lIGMoIm1vdG9yd2F5IiwidHJ1bmsiKSkKbWVkX3JvYWRfMDIgPC0gbWVkX2hpZ2h3YXlfbGluZXMgJT4lCiAgZmlsdGVyKGhpZ2h3YXkgJWluJSBjKCJwcmltYXJ5IiwgInNlY29uZGFyeSIpKQptZWRfcm9hZF8wMyA8LSBtZWRfaGlnaHdheV9saW5lcyAlPiUKICBmaWx0ZXIoaGlnaHdheSAlaW4lIGMoInRlcnRpYXJ5IikpCgpyb2FkX2xheWVyIDwtIGdlbmVyYXRlX2xpbmVfb3ZlcmxheShtZWRfcm9hZF8wMyxleHRlbnQgPSBleHRlbnRfem9vbWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAyLCBjb2xvcj0id2hpdGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0KSAlPiUgCiAgYWRkX292ZXJsYXkoZ2VuZXJhdGVfbGluZV9vdmVybGF5KG1lZF9yb2FkXzAyLGV4dGVudCA9IGV4dGVudF96b29tZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEsIGNvbG9yPSJ3aGl0ZSIsIGx0eT0zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHRtYXAgPSBlbGV2X21lZF9tYXQpKSAlPiUgCiAgYWRkX292ZXJsYXkoZ2VuZXJhdGVfbGluZV9vdmVybGF5KG1lZF9yb2FkXzAxLGV4dGVudCA9IGV4dGVudF96b29tZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDMsIGNvbG9yPSJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodG1hcCA9IGVsZXZfbWVkX21hdCkpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGNyZWF0ZSB3YXRlcmF5IGxheWVyCm1lZF9yaXZlcl8wMSA8LSBtZWRfcml2ZXJfbGluZXMgJT4lCiAgZmlsdGVyKHdhdGVyd2F5ICVpbiUgYygicml2ZXIiKSkKbWVkX3JpdmVyXzAyIDwtIG1lZF9yaXZlcl9saW5lcyAlPiUKICBmaWx0ZXIod2F0ZXJ3YXkgJWluJSBjKCJzdHJlYW0iKSkKCndhdGVyX2xheWVyIDwtIGdlbmVyYXRlX2xpbmVfb3ZlcmxheShtZWRfcml2ZXJfMDEsZXh0ZW50ID0gZXh0ZW50X3pvb21lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDIsIGNvbG9yPSJza3libHVlMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0KSAlPiUKICBhZGRfb3ZlcmxheShnZW5lcmF0ZV9saW5lX292ZXJsYXkobWVkX3JpdmVyXzAyLGV4dGVudCA9IGV4dGVudF96b29tZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEsIGNvbG9yPSJza3libHVlMiIsIGx0eT0zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHRtYXAgPSBlbGV2X21lZF9tYXQpKQpgYGAKCgpgYGB7cn0KIyBwb2x5Z29uX2xheWVyID0gZ2VuZXJhdGVfcG9seWdvbl9vdmVybGF5KG1lZF9wYXJraW5nX3BvbHlnb25zLCBleHRlbnQgPSBleHRlbnRfem9vbWVkLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0LCBwYWxldHRlPSJncmV5MzAiKSAlPiUKIyAgIGFkZF9vdmVybGF5KGdlbmVyYXRlX3BvbHlnb25fb3ZlcmxheShtZWRfYnVpbGRpbmdfcG9seWdvbnMsIGV4dGVudCA9IGV4dGVudF96b29tZWQsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0LCBwYWxldHRlPSJkYXJrcmVkIikpICU+JSAKIyAgIGFkZF9vdmVybGF5KGdlbmVyYXRlX3BvbHlnb25fb3ZlcmxheShtZWRfdG91cmlzbV9wb2x5Z29ucywgZXh0ZW50ID0gZXh0ZW50X3pvb21lZCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHRtYXAgPSBlbGV2X21lZF9tYXQsIHBhbGV0dGU9ImRhcmtncmVlbiIpLCBhbHBoYWxheWVyID0gMC42KQpwb2x5Z29uX2xheWVyID0gZ2VuZXJhdGVfcG9seWdvbl9vdmVybGF5KG1lZF9idWlsZGluZ19wb2x5Z29ucywgZXh0ZW50ID0gZXh0ZW50X3pvb21lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0LCBwYWxldHRlPSJkYXJrcmVkIikKYGBgCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpiYXNlX21hcCAlPiUgCiMgIGFkZF9vdmVybGF5KHBvbHlnb25fbGF5ZXIpICU+JQogIGFkZF9vdmVybGF5KHdhdGVyX2xheWVyLCBhbHBoYWxheWVyID0gMC44KSAlPiUgCiAgYWRkX292ZXJsYXkocm9hZF9sYXllcikgJT4lCiAgYWRkX292ZXJsYXkoZ2VuZXJhdGVfbGFiZWxfb3ZlcmxheShtZWRfcGxhY2VzX3BvaW50cywgZXh0ZW50ID0gZXh0ZW50X3pvb21lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRfc2l6ZSA9IDEsIHBvaW50X3NpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYWxvX2NvbG9yID0gImJsYWNrIiwgaGFsb19leHBhbmQgPSAxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYWxvX2JsdXIgPSAyMCwgaGFsb19hbHBoYSA9IDAuOCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQ9MSwgaGVpZ2h0bWFwID0gZWxldl9tZWRfbWF0LCBkYXRhX2xhYmVsX2NvbHVtbiA9ICJuYW1lLmVuIikpICU+JQogIHBsb3RfbWFwKCkKYGBgCgoKYGBge3J9CmJhc2VfbWFwICU+JSAKIyAgYWRkX292ZXJsYXkocG9seWdvbl9sYXllcikgJT4lCiAgYWRkX292ZXJsYXkod2F0ZXJfbGF5ZXIsIGFscGhhbGF5ZXIgPSAwLjgpICU+JSAKICBhZGRfb3ZlcmxheShyb2FkX2xheWVyKSAlPiUKICBhZGRfb3ZlcmxheShnZW5lcmF0ZV9sYWJlbF9vdmVybGF5KG1lZF9wbGFjZXNfcG9pbnRzLCBleHRlbnQgPSBleHRlbnRfem9vbWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9zaXplID0gMSwgcG9pbnRfc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhhbG9fY29sb3IgPSAiYmxhY2siLCBoYWxvX2V4cGFuZCA9IDEwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhhbG9fYmx1ciA9IDE1LCBoYWxvX2FscGhhID0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZD0xLCBoZWlnaHRtYXAgPSBlbGV2X21lZF9tYXQsIGRhdGFfbGFiZWxfY29sdW1uID0gIm5hbWUuZW4iKSkgJT4lCiAgcGxvdF8zZChlbGV2X21lZF9tYXQsIHdpbmRvd3NpemU9YygxMjAwLDExMDApLCB6c2NhbGU9MjAsIHRoZXRhPTIwLCBwaGk9MzAsIGZvdj00NSwgem9vbT0wLjYpCmBgYAoK